////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2005-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package com.swinginwind.flexutils.components
{
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.EventPhase;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.ui.Keyboard;
	
	import mx.collections.IList;
	import mx.containers.BoxDirection;
	import mx.controls.Button;
	import mx.controls.NavBar;
	import mx.controls.buttonBarClasses.ButtonBarButton;
	import mx.core.ClassFactory;
	import mx.core.EdgeMetrics;
	import mx.core.FlexVersion;
	import mx.core.IFactory;
	import mx.core.IFlexDisplayObject;
	import mx.core.IFlexModuleFactory;
	import mx.core.IUIComponent;
	import mx.core.mx_internal;
	import mx.events.ChildExistenceChangedEvent;
	import mx.events.ItemClickEvent;
	import mx.managers.IFocusManagerComponent;
	import mx.styles.CSSSelector;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.IStyleManager2;
	
	use namespace mx_internal;
	
	//--------------------------------------
	//  Events
	//--------------------------------------
	
	/**
	 *  Dispatched when a user clicks a button.
	 *  This event is only dispatched if the <code>dataProvider</code> property
	 *  does not refer to a ViewStack container.
	 *
	 *  @eventType mx.events.ItemClickEvent.ITEM_CLICK
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Event(name="itemClick", type="mx.events.ItemClickEvent")]
	
	//--------------------------------------
	//  Styles
	//--------------------------------------
	
	/**
	 *  Height of each button, in pixels.
	 *  If undefined, the height of each button is determined by the font styles
	 *  applied to the container.
	 *  If you set this property, the specified value overrides this calculation.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="buttonHeight", type="Number", format="Length", inherit="no")]
	
	/**
	 *  Name of CSS style declaration that specifies styles for the buttons.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="buttonStyleName", type="String", inherit="no")]
	
	/**
	 *  Width of each button, in pixels.
	 *  If undefined, the default width of each button is calculated from its label text.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="buttonWidth", type="Number", format="Length", inherit="no")]
	
	/**
	 *  Name of CSS style declaration that specifies styles for the first button.
	 *  If this is unspecified, the default value
	 *  of the <code>buttonStyleName</code> style property is used.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="firstButtonStyleName", type="String", inherit="no")]
	
	/**
	 *  Horizontal alignment of all buttons within the ButtonBar. Since individual 
	 *  buttons stretch to fill the entire ButtonBar, this style is only useful if you
	 *  use the buttonWidth style and the combined widths of the buttons are less than
	 *  than the width of the ButtonBar.
	 *  Possible values are <code>"left"</code>, <code>"center"</code>,
	 *  and <code>"right"</code>.
	 *
	 *  @default "center"
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="horizontalAlign", type="String", enumeration="left,center,right", inherit="no")]
	
	/**
	 *  Number of pixels between children in the horizontal direction.
	 *
	 *  The default value for the Halo theme is <code>0</code>.
	 *  The default value for the Spark theme is <code>-1</code>.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="horizontalGap", type="Number", format="Length", inherit="no")]
	
	/**
	 *  Name of CSS style declaration that specifies styles for the last button.
	 *  If this is unspecified, the default value
	 *  of the <code>buttonStyleName</code> style property is used.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="lastButtonStyleName", type="String", inherit="no")]
	
	/**
	 *  Vertical alignment of all buttons within the ButtonBar. Since individual 
	 *  buttons stretch to fill the entire ButtonBar, this style is only useful if you
	 *  use the buttonHeight style and the combined heights of the buttons are less than
	 *  than the width of the ButtonBar.
	 *  Possible values are <code>"top"</code>, <code>"middle"</code>,
	 *  and <code>"bottom"</code>.
	 *
	 *  @default "middle"
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="verticalAlign", type="String", enumeration="top,middle,bottom", inherit="no")]
	
	/**
	 *  Number of pixels between children in the vertical direction.
	 *
	 *  @default 0
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	[Style(name="verticalGap", type="Number", format="Length", inherit="no")]
	
	//--------------------------------------
	//  Excluded APIs
	//--------------------------------------
	
	[Exclude(name="horizontalLineScrollSize", kind="property")]
	[Exclude(name="horizontalPageScrollSize", kind="property")]
	[Exclude(name="horizontalScrollBar", kind="property")]
	[Exclude(name="horizontalScrollPolicy", kind="property")]
	[Exclude(name="horizontalScrollPosition", kind="property")]
	[Exclude(name="maxHorizontalScrollPosition", kind="property")]
	[Exclude(name="maxVerticalScrollPosition", kind="property")]
	[Exclude(name="verticalLineScrollSize", kind="property")]
	[Exclude(name="verticalPageScrollSize", kind="property")]
	[Exclude(name="verticalScrollBar", kind="property")]
	[Exclude(name="verticalScrollPolicy", kind="property")]
	[Exclude(name="verticalScrollPosition", kind="property")]
	
	[Exclude(name="scroll", kind="event")]
	[Exclude(name="click", kind="event")]
	
	[Exclude(name="backgroundAlpha", kind="style")]
	[Exclude(name="backgroundAttachment", kind="style")]
	[Exclude(name="backgroundColor", kind="style")]
	[Exclude(name="backgroundImage", kind="style")]
	[Exclude(name="backgroundSize", kind="style")]
	[Exclude(name="borderColor", kind="style")]
	[Exclude(name="borderSides", kind="style")]
	[Exclude(name="borderSkin", kind="style")]
	[Exclude(name="borderStyle", kind="style")]
	[Exclude(name="borderThickness", kind="style")]
	[Exclude(name="cornerRadius", kind="style")]
	[Exclude(name="dropShadowColor", kind="style")]
	[Exclude(name="dropShadowEnabled", kind="style")]
	[Exclude(name="horizontalScrollBarStyleName", kind="style")]
	[Exclude(name="shadowCapColor", kind="style")]
	[Exclude(name="shadowColor", kind="style")]
	[Exclude(name="shadowDirection", kind="style")]
	[Exclude(name="shadowDistance", kind="style")]
	[Exclude(name="verticalScrollBarStyleName", kind="style")]
	
	//--------------------------------------
	//  Other metadata
	//--------------------------------------
	
	[DefaultProperty("dataProvider")]
	
	[DefaultTriggerEvent("itemClick")]
	
	
	[Alternative(replacement="spark.components.ButtonBar", since="4.0")]
	
	[MaxChildren(0)]
	
	/**
	 *  The ButtonBar control defines a horizontal or vertical group of 
	 *  logically related push buttons with a common look and navigation.
	 *
	 *  <p>A push button is one that does not remember its selected state
	 *  when selected.
	 *  The typical use for a push button in a button bar is for grouping
	 *  a set of related buttons together, which gives them a common look
	 *  and navigation, and handling the logic for the <code>click</code> event
	 *  in a single place. </p>
	 *
	 *  <p>The ButtonBar control creates Button controls based on the value of 
	 *  its <code>dataProvider</code> property. 
	 *  Even though ButtonBar is a subclass of Container, do not use methods such as 
	 *  <code>Container.addChild()</code> and <code>Container.removeChild()</code> 
	 *  to add or remove Button controls. 
	 *  Instead, use methods such as <code>addItem()</code> and <code>removeItem()</code> 
	 *  to manipulate the <code>dataProvider</code> property. 
	 *  The ButtonBar control automatically adds or removes the necessary children based on 
	 *  changes to the <code>dataProvider</code> property.</p>
	 *
	 *  <p>To control the styling of the buttons of the ButtonBar control, use the 
	 *  <code>buttonStyleName</code>, <code>firstButtonStyleName</code>, 
	 *  and <code>lastButtonStyleName</code> style properties; 
	 *  do not try to style the individual Button controls 
	 *  that make up the ButtonBar control.</p>
	 *
	 *  <p>You can use the ToggleButtonBar control to define a group
	 *  of toggle buttons.</p>
	 *
	 *  <p>ButtonBar control has the following default characteristics:</p>
	 *  <table class="innertable">
	 *     <tr>
	 *        <th>Characteristic</th>
	 *        <th>Description</th>
	 *     </tr>
	 *     <tr>
	 *        <td>Preferred size</td>
	 *        <td>Wide enough to contain all buttons with their label text and icons, if any, plus any padding and separators, and high enough to accommodate the button height.</td>
	 *     </tr>
	 *     <tr>
	 *        <td>Control resizing rules</td>
	 *        <td>The controls do not resize by default. Specify percentage sizes if you want your ButtonBar to resize based on the size of its parent container.</td>
	 *     </tr>
	 *     <tr>
	 *        <td>Padding</td>
	 *        <td>0 pixels for the top, bottom, left, and right properties.</td>
	 *     </tr>
	 *  </table>
	 *
	 *  @mxml
	 *
	 *  <p>The <code>&lt;mx:ButtonBar&gt;</code> tag inherits all the tag attributes
	 *  of its superclass, and adds the following tag attributes:</p>
	 *  
	 *  <pre>
	 *  &lt;mx:ButtonBar
	 *    <b>Styles</b>
	 *    buttonHeight="undefined"
	 *    buttonStyleName="<i>Name of CSS style declaration, which specifies
	 *    styles for the buttons</i>"
	 *    buttonWidth="undefined"
	 *    firstButtonStyleName="<i>The value of</i> <code>buttonStyleName</code>"
	 *    horizontalAlign="center|left|right"
	 *    horizontalGap="0"
	 *    lastButtonStyleName="<i>The value of</i> <code>buttonStyleName</code>"
	 *    verticalAlign="middle|top|bottom"
	 *    verticalGap="0"
	 *     
	 *    <b>Events</b>
	 *    itemClick="<i>No default</i>"
	 *    &gt;
	 *    ...
	 *       <i>child tags</i>
	 *    ...
	 *  &lt;/mx:ButtonBar&gt;
	 *  </pre>
	 *
	 *  @see mx.controls.ToggleButtonBar
	 *  @see mx.controls.LinkBar
	 *  @includeExample examples/ButtonBarExample.mxml
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	public class CustomButtonBar extends NavBar implements IFocusManagerComponent
	{
		//include "../core/Version.as";
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  Constructor.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 9
		 *  @playerversion AIR 1.1
		 *  @productversion Flex 3
		 */
		public function CustomButtonBar()
		{
			super();
			
			tabEnabled = true;
			tabFocusEnabled = true;
			
			navItemFactory = new ClassFactory(ButtonBarButton);
			
			// Add event listeners for scaleX/scaleY changed.
			// Since we hard-code sizes into our children,
			// scaling can cause rounding errors so we need
			// to clear our hard-coded values whenever the scale changes.
			addEventListener("scaleXChanged", scaleChangedHandler);
			addEventListener("scaleYChanged", scaleChangedHandler);
			
			addEventListener(ChildExistenceChangedEvent.CHILD_REMOVE, 
				childRemoveHandler);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 *  Internal flag to indicate when a click event has been triggered
		 *  programmatically, as opposed to an actual user click.
		 *  This happens when the button selection happens by keyboard navigation
		 *  or when selectedIndex is set programmatically.
		 *  When this is true, the focus rect shouldn't be drawn
		 *  for the currently selected button.
		 */
		mx_internal var simulatedClickTriggerEvent:Event = null;
		
		/**
		 *  @private
		 *  Name of style used to specify buttonStyleName.
		 *  Overridden by TabBar.
		 */
		mx_internal var buttonStyleNameProp:String = "buttonStyleName";
		
		/**
		 *  @private
		 *  Name of style used to specify buttonStyleName.
		 *  Overridden by TabBar.
		 */
		mx_internal var firstButtonStyleNameProp:String = "firstButtonStyleName";
		
		/**
		 *  @private
		 *  Name of style used to specify buttonStyleName.
		 *  Overridden by TabBar.
		 */
		mx_internal var lastButtonStyleNameProp:String = "lastButtonStyleName";
		
		/**
		 *  @private
		 *  Name of style used to specify buttonWidth.
		 *  Overridden by TabBar.
		 */
		mx_internal var buttonWidthProp:String = "buttonWidth";
		
		/**
		 *  @private
		 *  Name of style used to specify buttonHeight.
		 *  Overridden by TabBar.
		 */
		mx_internal var buttonHeightProp:String = "buttonHeight";
		
		/**
		 *  @private
		 *  Flag indicating whether buttons widths should be recalculated.
		 */
		private var recalcButtonWidths:Boolean = false;
		
		/**
		 *  @private
		 *  Flag indicating whether buttons heights should be recalculated.
		 */
		private var recalcButtonHeights:Boolean = false;
		
		/**
		 *  @private
		 *  The value of the unscaledWidth parameter during the most recent
		 *  call to updateDisplayList
		 */
		private var oldUnscaledWidth:Number;
		
		/**
		 *  @private
		 *  The value of the unscaledHeight parameter during the most recent
		 *  call to updateDisplayList
		 */
		private var oldUnscaledHeight:Number;
		
		/**
		 *  @private
		 *  Index of currently focused child.
		 */
		mx_internal var focusedIndex:int = 0;
		
		/**
		 *  @private
		 *  Flag indicating whether direction has changed.
		 */
		private var directionChanged:Boolean = false;
		
		
		private var _itemRenderer:IFactory;
		
		//--------------------------------------------------------------------------
		//
		//  Overridden properties
		//
		//--------------------------------------------------------------------------
		
		//----------------------------------
		//  borderMetrics
		//----------------------------------
		
		public function get itemCallback():Function
		{
			return _itemCallback;
		}

		public function set itemCallback(value:Function):void
		{
			_itemCallback = value;
		}

		public function get itemRenderer():IFactory
		{
			return _itemRenderer;
		}
		
		public function set itemRenderer(value:IFactory):void
		{
			_itemRenderer = value;
			super.navItemFactory = value;
		}
		
		/**
		 * 设置item后callback可以进行一些操作
		 */
		private var _itemCallback:Function;
		
		
		
		/**
		 *  @private
		 */
		override public function get borderMetrics():EdgeMetrics
		{
			return EdgeMetrics.EMPTY;
		}
		
		//----------------------------------
		//  direction
		//----------------------------------
		
		[Bindable("directionChanged")]
		[Inspectable(category="General", enumeration="vertical,horizontal", defaultValue="horizontal")]
		
		/**
		 *  @private
		 */
		override public function set direction(value:String):void
		{
			if (initialized && value != direction)
			{
				directionChanged = true;
				invalidateProperties();
			}
			
			super.direction = value;
		}
		
		//----------------------------------
		//  viewMetrics
		//----------------------------------
		
		/**
		 *  @private
		 */
		override public function get viewMetrics():EdgeMetrics
		{
			return EdgeMetrics.EMPTY;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Overridden methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		override public function styleChanged(styleProp:String):void
		{
			var allStyles:Boolean = styleProp == null || styleProp == "styleName";
			
			super.styleChanged(styleProp);
			
			if (allStyles ||
				styleProp == buttonStyleNameProp ||
				styleProp == firstButtonStyleNameProp ||
				styleProp == lastButtonStyleNameProp)
			{
				resetNavItems();
			}
			else if (styleProp == buttonWidthProp)
			{
				recalcButtonWidths = true;
			}
			else if (styleProp == buttonHeightProp)
			{
				recalcButtonHeights = true;
			}
			else if (styleManager.isInheritingStyle(styleProp) && 
				styleManager.isSizeInvalidatingStyle(styleProp))
			{
				recalcButtonWidths = recalcButtonHeights = true;
			}        
		}
		
		/**
		 *  @private
		 */
		override protected function commitProperties():void
		{
			super.commitProperties();
			
			if (directionChanged)
			{
				directionChanged = false;
				
				// refresh skins
				var n:int = numChildren;
				for (var i:int = 0; i < n; i++)
					Button(getChildAt(i)).changeSkins();
			}
			
			// Buttons widths and/or heights must be reset before measurement. We do
			// not reset these flags here since they will be used in updateDisplayList().
			if (recalcButtonHeights)
				resetButtonHeights();
			
			if (recalcButtonWidths)
				resetButtonWidths();    
		}
		
		/**
		 *  @private
		 */
		override protected function measure():void
		{
			super.measure();
			
			var vm:EdgeMetrics = viewMetricsAndPadding;
			
			measuredWidth = calcFullWidth() + vm.left + vm.right;
			measuredHeight = calcFullHeight() + vm.top + vm.bottom;
			
			// If explicit button sizes are specified, our preferred sizes are our
			// minimum sizes.
			if (getStyle(buttonWidthProp))
				measuredMinWidth = measuredWidth;
			if (getStyle(buttonHeightProp))
				measuredMinHeight = measuredHeight;
			
		}
		
		/**
		 *  @private
		 */
		override protected function updateDisplayList(unscaledWidth:Number,
													  unscaledHeight:Number):void
		{
			// We pre-process our child sizes, so we call super.updateDisplayList() later.
			
			const isHorizontal:Boolean = (direction == BoxDirection.HORIZONTAL);
			const isVertical:Boolean = !isHorizontal;
			
			var buttonWidth:Number = getStyle(buttonWidthProp);
			var buttonHeightStyle:Number = getStyle(buttonHeightProp);
			var buttonHeight:Number = buttonHeightStyle;
			
			var vm:EdgeMetrics = viewMetricsAndPadding;
			
			var n:int = numChildren;
			var horizontalGap:Number = getStyle("horizontalGap");
			var verticalGap:Number = getStyle("verticalGap");
			var totalHorizontalGap:Number = isHorizontal && numChildren > 0 ? 
				horizontalGap * (n - 1) : 0;
			var totalVerticalGap:Number = isVertical && numChildren > 0 ? 
				verticalGap * (n - 1) : 0;
			var w:Number = unscaledWidth - vm.left - vm.right - totalHorizontalGap;
			var h:Number = unscaledHeight - vm.top - vm.bottom - totalVerticalGap;
			if (!w || !h)
				return;
			
			if (border)
				border.visible = false;
			
			if (unscaledWidth != oldUnscaledWidth)
			{
				recalcButtonWidths = true;
				oldUnscaledWidth = unscaledWidth;
			}
			
			if (unscaledHeight != oldUnscaledHeight)
			{
				recalcButtonHeights = true;
				oldUnscaledHeight = unscaledHeight;
			}
			
			var i:int;
			var c:Button;
			
			var excessSpace:Number;
			
			// See if we need to recalculate the button widths
			if (recalcButtonWidths)
			{
				recalcButtonWidths = false;
				
				if (isNaN(buttonWidth) && isVertical)
					buttonWidth = w;
				
				excessSpace = w - (calcFullWidth() - totalHorizontalGap);
				var averageWidth:int = n > 0 ? w / n : 0;
				// Number of children larger than average width
				var nLarge:int = 0;
				
				// Sum of all preferred widths (required if excessSpace != 0)
				var tw:Number = 0;
				// Sum of all preferred widths of children
				// smaller than average width.
				var tSmall:int = 0;
				if (excessSpace != 0 && isHorizontal)
				{
					for (i = 0; i < n; i++)
					{
						c = Button(getChildAt(i));
						if (isNaN(c.explicitWidth))
						{
							var mw:int = c.measuredWidth;
							tw += mw;
							if (mw > averageWidth)
								nLarge++;
							else
								tSmall += mw;
						}
					}
				}
				else
				{
					tw = w;
				}
				
				for (i = 0; i < n; i++)
				{
					c = Button(getChildAt(i));
					if (isNaN(c.explicitWidth))
					{
						c.minWidth = 0;
						if (!isNaN(buttonWidth))
						{
							c.minWidth = c.maxWidth = buttonWidth;
							c.percentWidth = buttonWidth / Math.min(w, tw) * 100;
						}
							
							// Assign measured width to children smaller than average.
							// Distribute the remaining width to others.
						else if (excessSpace < 0)
						{
							var assignedWidth:Number = c.measuredWidth;
							if (assignedWidth > averageWidth)
								assignedWidth = (w - tSmall) / nLarge;
							
							c.percentWidth = assignedWidth / w * 100;
						}
							
							// If they fit comfortably with extra space left,
							// expand them.
						else if (excessSpace > 0)
						{
							c.percentWidth = c.measuredWidth / tw * 100;
						}
							
						else
						{
							c.percentWidth = NaN;
						}
						
						// If vertical, expand to fit horizontally.
						if (isVertical)
							c.percentWidth = 100;
					}
				}
			}
			
			// See if we need to recalculate the button heights
			if (recalcButtonHeights)
			{
				recalcButtonHeights = false;
				
				if (isNaN(buttonHeight) && isHorizontal)
					buttonHeight = h;
				
				excessSpace = h - (calcFullHeight() - totalVerticalGap);
				
				// Sum of all preferred heights (required if excessSpace != 0).
				var th:Number = 0;
				if (excessSpace != 0 && isVertical)
				{
					for (i = 0; i < n; i++)
					{
						c = Button(getChildAt(i));
						if (isNaN(c.explicitHeight))
							th += c.measuredHeight;
					}
				}
				
				for (i = 0; i < n; i++)
				{
					c = Button(getChildAt(i));
					if (isNaN(c.explicitHeight))
					{
						c.minHeight = 0;
						if (!isNaN(buttonHeight))
						{
							c.minHeight = buttonHeight;
							c.percentHeight = buttonHeight / Math.min(th, h) * 100;
						}
						if (!isNaN(buttonHeightStyle))
							c.maxHeight = buttonHeightStyle;
						
						// If horizontal, expand to fit vertically.
						if (isHorizontal)
							c.percentHeight = 100;
							
							// If they won't fit, squeeze them in.
						else if (excessSpace < 0)
							c.percentHeight = c.measuredHeight / th * 100;
							
							// If they fit comfortably with extra space left,
							// expand them.
						else if (excessSpace > 0)
							c.percentHeight = c.measuredHeight / th * 100;
							
						else
							c.percentHeight = NaN;
					}
				}
			}
			
			// Since we pre-process our child dimensions, we call super.updateDisplayList()
			// last.
			super.updateDisplayList(unscaledWidth, unscaledHeight);
		}
		
		/**
		 *  @private
		 */
		override public function drawFocus(isFocused:Boolean):void
		{
			drawButtonFocus(focusedIndex, isFocused);
		}
		
		/**
		 *  @private
		 */
		override protected function createNavItem(
			label:String,
			icon:Class = null):IFlexDisplayObject
		{
			var newButton:Button = Button(navItemFactory.newInstance());
			
			// Set focusEnabled to false so individual buttons don't get focus.
			newButton.focusEnabled = false;
			newButton.label = label;
			newButton.setStyle("icon", icon);
			newButton.addEventListener(MouseEvent.CLICK, clickHandler);
			
			addChild(newButton);
			
			recalcButtonWidths = recalcButtonHeights = true;
			
			return newButton;
		}
		
		/**
		 *  @private
		 */
		override protected function resetNavItems():void
		{
			var buttonStyleName:String = getStyle(buttonStyleNameProp);
			var firstButtonStyleName:String = getStyle(firstButtonStyleNameProp);
			var lastButtonStyleName:String = getStyle(lastButtonStyleNameProp);
			
			if (!buttonStyleName)
				buttonStyleName = "ButtonBarButton";
			if (!firstButtonStyleName)
				firstButtonStyleName = buttonStyleName;
			if (!lastButtonStyleName)
				lastButtonStyleName = buttonStyleName;
			
			var button:Button;
			var n:int = numChildren;
			for (var i:int = 0; i < n; i++)
			{		
				button = Button(getChildAt(i));
				if(dataProvider is IList)
					button.data = IList(dataProvider).getItemAt(i);
				if(itemCallback != null)
					itemCallback(button);
				if (i == 0)
				{
					button.styleName = firstButtonStyleName;
				}
				else if (i == (n - 1))
				{
					button.styleName = lastButtonStyleName;
				}
				else
				{
					button.styleName = buttonStyleName;
				}
				
				button.changeSkins();
				button.invalidateDisplayList();
			}
			
			recalcButtonWidths = recalcButtonHeights = true;
			
			invalidateDisplayList();
		}
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		private function calcFullWidth():Number
		{
			var n:int = numChildren;
			var gap:Number = 0;
			
			if (n == 0)
				return 0;
			else if (n > 1)
				gap = getStyle("horizontalGap");
			
			var horizontal:Boolean = (direction == BoxDirection.HORIZONTAL);
			
			var buttonWidth:Number = getStyle(buttonWidthProp);
			
			var child:IUIComponent = IUIComponent(getChildAt(0));
			
			var w:Number;
			if (buttonWidth)
				w = isNaN(child.explicitWidth) ? buttonWidth : child.explicitWidth;
			else
				w = child.getExplicitOrMeasuredWidth();
			
			for (var i:int = 1; i < n; i++)
			{
				child = IUIComponent(getChildAt(i));
				
				var cw:Number;
				if (buttonWidth)
				{
					cw = isNaN(child.explicitWidth) ?
						buttonWidth :
						child.explicitWidth;
				}
				else
				{
					cw = child.getExplicitOrMeasuredWidth();
				}
				
				if (horizontal)
					w += (gap + cw);
				else
					w = Math.max(w, cw);
			}
			
			return w;
		}
		
		/**
		 *  @private
		 */
		private function calcFullHeight():Number
		{
			var n:int = numChildren;
			var gap:Number;
			
			if (n == 0)
				return 0;
			else if (n > 1)
				gap = getStyle("verticalGap");
			
			var vertical:Boolean = (direction == BoxDirection.VERTICAL);
			
			var buttonHeight:Number = getStyle(buttonHeightProp);
			
			var child:IUIComponent = IUIComponent(getChildAt(0));
			
			var h:Number;
			if (buttonHeight)
			{
				h = isNaN(child.explicitHeight) ?
					buttonHeight :
					child.explicitHeight;
			}
			else
			{
				h = child.getExplicitOrMeasuredHeight();
			}
			
			for (var i:int = 1; i < n; i++)
			{
				child = IUIComponent(getChildAt(i));
				
				var ch:Number;
				if (buttonHeight)
				{
					ch = isNaN(child.explicitHeight) ?
						buttonHeight :child.explicitHeight;
				}
				else
				{
					ch = child.getExplicitOrMeasuredHeight();
				}
				
				if (vertical)
					h += (gap + ch);
				else
					h = Math.max(h, ch);
			}
			
			return h;
		}
		
		/**
		 *  @private
		 *  Returns the previous valid child index, or -1 if there are no children.
		 *  Used by keyboard navigation.
		 */
		mx_internal function prevIndex(index:int):int
		{
			var n:int = numChildren;
			return index == 0 ? n - 1 : index - 1;
		}
		
		/**
		 *  @private
		 *  Returns the next valid child index, or -1 if there are no children.
		 *  Used by keyboard navigation.
		 */
		mx_internal function nextIndex(index:int):int
		{
			var n:int = numChildren;
			if (n == 0)
				return -1;
			else
				return index == n - 1 ? 0 : index + 1;
		}
		
		/**
		 *  @private
		 */
		mx_internal function drawButtonFocus(index:int, focused:Boolean):void
		{
			if (numChildren > 0 && index < numChildren)
			{
				var b:Button = Button(getChildAt(index));
				b.drawFocus(focused && focusManager.showFocusIndicator);
				
				// internal event for accessibility
				if (focused)
					dispatchEvent(new Event("focusDraw"));
				
				// If the button is losing focus, set its phase to UP,
				// which will cause a redraw.
				if (!focused && b.phase != "up")
					b.phase = "up";
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Overridden event handlers: UIComponent
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		override protected function keyDownHandler(event:KeyboardEvent):void
		{
			// Ignore events that bubble up from the child ButtonBarButtons.
			// such as the one we redispatch below from the focused child
			// when the SPACE key is released.
			if (event.eventPhase != EventPhase.AT_TARGET)
				return;
			
			// If rtl layout, need to swap LEFT and RIGHT so correct action
			// is done.
			var keyCode:uint = mapKeycodeForLayoutDirection(event, true);
			
			switch (keyCode)
			{
				case Keyboard.DOWN:
				case Keyboard.RIGHT:
				{
					focusManager.showFocusIndicator = true;
					drawButtonFocus(focusedIndex, false);
					focusedIndex = nextIndex(focusedIndex);
					
					if (focusedIndex != -1)
					{
						drawButtonFocus(focusedIndex, true);
					}
					
					event.stopPropagation();
					break;
				}
					
				case Keyboard.UP:
				case Keyboard.LEFT:
				{
					focusManager.showFocusIndicator = true;
					drawButtonFocus(focusedIndex, false);
					focusedIndex = prevIndex(focusedIndex);
					
					if (focusedIndex != -1)
					{
						drawButtonFocus(focusedIndex, true);
					}
					
					event.stopPropagation();
					break;
				}
					
				case Keyboard.SPACE:
				{
					if (focusedIndex != -1)
					{
						// Redispatch from the focused ButtonBarButton
						// to get it to appear pressed.
						var child:Button = Button(getChildAt(focusedIndex));
						child.dispatchEvent(event);
					}
					
					event.stopPropagation();
					break;
				}
			}
		}
		
		/**
		 *  @private
		 */
		override protected function keyUpHandler(event:KeyboardEvent):void
		{
			// Ignore events that bubble up from the child ButtonBarButtons.
			// such as the one we redispatch below from the focused child
			// when the SPACE key is released.
			if (event.eventPhase != EventPhase.AT_TARGET)
				return;
			
			switch (event.keyCode)
			{
				case Keyboard.SPACE:
				{
					if (focusedIndex != -1)
					{
						// Redispatch from the focused ButtonBarButton
						// to get it to appear released.
						var child:Button = Button(getChildAt(focusedIndex));
						child.dispatchEvent(event);
					}
					
					event.stopPropagation();
					break;
				}
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Overridden event handlers: NavBar
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		override protected function clickHandler(event:MouseEvent):void
		{
			if (simulatedClickTriggerEvent == null)
			{
				focusedIndex = getChildIndex(DisplayObject(event.currentTarget));
				drawButtonFocus(focusedIndex, true);
			}
			
			super.clickHandler(event);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		private function childRemoveHandler(event:ChildExistenceChangedEvent):void
		{   
			var child:DisplayObject = event.relatedObject;
			var index:int = getChildIndex(child);
			var n:int = numChildren;
			if (n < 2)
			{
				// Don't bother if it's the last child.
				return;
			}
			
			var buttonStyleName:String = getStyle(buttonStyleNameProp);
			var firstButtonStyleName:String = getStyle(firstButtonStyleNameProp);
			var lastButtonStyleName:String = getStyle(lastButtonStyleNameProp);
			
			if (!buttonStyleName)
				buttonStyleName = "buttonBarButtonStyle";
			if (!firstButtonStyleName)
				firstButtonStyleName = buttonStyleName;
			if (!lastButtonStyleName)
				lastButtonStyleName = buttonStyleName;
			
			// Refresh the skins for the last button that was in this position.
			if (index == 0 || index == n - 1)
			{
				var button:Button = Button(getChildAt(index == n - 1 ? n - 2 : 0));
				
				button.styleName = index == 0 ?
					firstButtonStyleName :
					lastButtonStyleName;
				
				button.changeSkins();
				button.invalidateDisplayList();
			}
		}
		
		/**
		 *  @private
		 *  Reset buttons widths so that it can be recalculated.
		 */
		protected function resetButtonWidths():void
		{
			for (var i:int = 0; i < numChildren; i++)
			{
				var child:Button = getChildAt(i) as Button;
				if (child)
				{
					child.explicitWidth = NaN;
					child.minWidth = NaN;
					child.maxWidth = NaN;
				}
			}
		}
		
		/**
		 *  @private
		 *  Reset buttons heights so that it can be recalculated.
		 */
		protected function resetButtonHeights():void
		{
			for (var i:int = 0; i < numChildren; i++)
			{
				var child:Button = getChildAt(i) as Button;
				if (child)
				{
					child.explicitHeight = NaN;
					child.minHeight = NaN;
					child.maxHeight = NaN;
				}
			}
		}
		
		/**
		 *  @private
		 */
		private function scaleChangedHandler(event:Event):void
		{
			// This is called whenever scaleX or scaleY is changed.
			// We need to clear out the preferredWidth/preferredHeight
			// of our children since scaling can cause rounding errors
			// which then are not corrected when un-scaled.  
			resetButtonHeights();
			resetButtonWidths();
		}
	}
	
}
